/*  -*- mode: c++; coding: utf-8; c-file-style: "stroustrup"; -*-

    Copyright 2008 Asier Aguirre <asier.aguirre@gmail.com>
    This file is part of Intromit.

    Intromit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Intromit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Intromit.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "pymetadata.hh"
#include "pymetadata_global.hh"
#include "gendata_common.hh"

#include <dlfcn.h>

// GLOBAL
pymetadata_global pymdg;

// DEFINITIONS
#define INC_DL              4
#define INC_TYPE           32
#define INC_METADATA        4
#define INC_OBJ_LIST     8192

#define MAX_STACK        1024
#define EVENT_QUEUE_SIZE 1024  // must be power of 2
#define EVENT_QUEUE_MASK     (EVENT_QUEUE_SIZE-1)
#define INC_EQ_OBJ         16
#define USEC_INTERVAL   10000

#define MAX_PYMD_BUFFER           1024

CLASS_INIT(pymetadata_global); // PYMETADATA_GLOBAL CLASS

pymetadata_global::pymetadata_global()
{
    CLASS_CTOR(pymetadata_global);

    util::ctor_vector(meta, n_meta, max_meta);
    util::ctor_vector(type, n_type, max_type);
    util::ctor_vector(dl, n_dl, max_dl);

    // python type metadata
    first_polymorphic_type=0;
    type_python_vector=NULL;

    // init error string
    util::ctor_vector(error_string, n_error_string, max_error_string);

    // type functions (could be done faster initializing it statically, but then vector would be position dependant)
    if(GENDATA_LAST) func=(type_function *)malloc(GENDATA_LAST*sizeof(type_function));
    struct
    {
	gendata_type gendata;
	type_function tf;
    } type_functions[]=
#     define GD_DO_TYPE3(X, x, m) {GENDATA_##X, {c_to_python_##x, python_to_c_##x, check_python_##x, NULL, m}}
#     define GD_DO_TYPE4(X, x, m) {GENDATA_##X, {c_to_python_##x, python_to_c_##x, check_python_##x, free_c_##x, m}}
      {
	  GD_DO_TYPE3(FLOAT,          float,          PYMD_MARSHALLING_RETURN_FLOAT),
	  GD_DO_TYPE3(DOUBLE,         double,         PYMD_MARSHALLING_RETURN_DOUBLE),
	  GD_DO_TYPE3(INT,            int,            PYMD_MARSHALLING_RETURN_INT),
	  GD_DO_TYPE3(UNSIGNED_INT,   unsigned_int,   PYMD_MARSHALLING_RETURN_INT),
	  GD_DO_TYPE3(SHORT,          short,          PYMD_MARSHALLING_RETURN_INT),
	  GD_DO_TYPE3(UNSIGNED_SHORT, unsigned_short, PYMD_MARSHALLING_RETURN_INT),
	  GD_DO_TYPE3(LONG_LONG,      long_long,      PYMD_MARSHALLING_RETURN_LONG_LONG),
	  GD_DO_TYPE3(UNSIGNED_LONG_LONG, ulong_long, PYMD_MARSHALLING_RETURN_LONG_LONG),
	  GD_DO_TYPE3(CHAR,           char,           PYMD_MARSHALLING_RETURN_INT),
	  GD_DO_TYPE3(UNSIGNED_CHAR,  unsigned_char,  PYMD_MARSHALLING_RETURN_INT),
	  GD_DO_TYPE4(OBJECT_POINTER, object_pointer, PYMD_MARSHALLING_RETURN_INT),
	  GD_DO_TYPE4(REFERENCE,      reference,      PYMD_MARSHALLING_RETURN_INT_DONOTFREE),
	  GD_DO_TYPE4(CHARP,          charp,          PYMD_MARSHALLING_RETURN_INT),
	  GD_DO_TYPE3(CONST_CHARP,    const_charp,    PYMD_MARSHALLING_RETURN_INT),
	  GD_DO_TYPE4(ARRAY_TYPE,     array_type,     PYMD_MARSHALLING_RETURN_INT),
	  GD_DO_TYPE3(BOOL,           boolean,        PYMD_MARSHALLING_RETURN_INT),
	  GD_DO_TYPE3(ENUM,           int,            PYMD_MARSHALLING_RETURN_INT),
	  GD_DO_TYPE3(CLASS,          class,          PYMD_MARSHALLING_RETURN_VOID/**/),
	  {GENDATA_LAST, {NULL, NULL, NULL, NULL,     PYMD_MARSHALLING_RETURN_LAST}}
      };
#     undef GD_DO_TYPE4
#     undef GD_DO_TYPE3

    for(int i=0; i<GENDATA_LAST; i++)
    {
	int j=0;
	while(type_functions[j].gendata!=GENDATA_LAST)
	{
	    if(type_functions[j].gendata==i)
	    {
		memcpy(&func[i], &type_functions[j].tf, sizeof(type_function));
		break;
	    }
	    j++;
	}
	if(type_functions[j].gendata==GENDATA_LAST)
	    memset(&func[i], 0, sizeof(type_function));
    }

    // python object list
    util::ctor_vector(obj_list, n_obj_list, max_obj_list);
    n_obj_list_vanished=0;

    // event queue
    util::ctor_vector(eq_obj, n_eq_obj, max_eq_obj);
    eq=(event_queue *)malloc(EVENT_QUEUE_SIZE*sizeof(event_queue));
    eq_ini=eq_end=0;
}

pymetadata_global::~pymetadata_global()
{
    CLASS_DTOR(pymetadata_global);
    dtor();

    // event queue
    free(eq);
}

void pymetadata_global::dtor()
{
    // event queue
    eq_ini=eq_end=0; // empty queue
    for(int i=0; i<n_eq_obj; i++)
    {
	if(eq_obj[i].obj) eq_obj[i].obj->python_decref();
	if(eq_obj[i].func_name) free(eq_obj[i].func_name);
    }
    util::dtor_vector(eq_obj, n_eq_obj, max_eq_obj);

    // metadata (gendata)
    for(int i=0; i<n_meta; i++)
    {
	if(meta[i].data) free(meta[i].data);
	if(meta[i].doc) free(meta[i].doc);
    }
    util::dtor_vector(meta, n_meta, max_meta);

    // types
    for(int i=0; i<n_type; i++) if(type[i]) delete type[i];
    util::dtor_vector(type, n_type, max_type);

    // dl handlers
    for(int i=0; i<n_dl; i++)
    {
	if(dl[i].elf_name) free(dl[i].elf_name);
	if(dl[i].dl) dlclose(dl[i].dl);
    }
    util::dtor_vector(dl, n_dl, max_dl);

    // functions
    if(func) {free(func); func=NULL;}

    // error string
    util::dtor_vector(error_string, n_error_string, max_error_string);

    // python object list
    if(n_obj_list) ERROR("obj_list not empty");
    util::dtor_vector(obj_list, n_obj_list, max_obj_list);
    n_obj_list_vanished=0;
}

int pymetadata_global::meta_exists(int *data, int n_data, char *doc, int n_doc)
{
    // this checksum is calculated before any modification of data or doc
    unsigned cs=util::fast_checksum(data, n_data) ^ util::fast_checksum(doc, n_doc);

    for(int i=0; i<n_meta; i++) if(cs==meta[i].checksum) return i;
    return -1;
}

int pymetadata_global::dl_handle_exists(char *elf_name)
{
    for(int i=0; i<n_dl; i++) if(!strcmp(elf_name, dl[i].elf_name)) return i;
    return -1;
}

int pymetadata_global::add_meta(int *data, int n_data, char *doc, int n_doc)
{
    int i=util::inc_vector(meta, n_meta, max_meta, INC_METADATA);
    meta[i].data=data;
    meta[i].n_data=n_data;
    meta[i].doc=doc;
    meta[i].n_doc=n_doc;
    // TODO: this checksum is calculated twice
    meta[i].checksum=util::fast_checksum(data, n_data) ^ util::fast_checksum(doc, n_doc);
    return i;
}

int pymetadata_global::sort_type_by_vtable_compar(const void *a, const void *b)
{
    return ((unsigned)(*(python_type_metadata**)b)->get_vtable() < (unsigned)(*(python_type_metadata**)a)->get_vtable());
}

void pymetadata_global::sort_type_by_vtable()
{
    qsort(type, n_type, sizeof(python_type_metadata *), sort_type_by_vtable_compar);
    while(first_polymorphic_type<n_type && !type[first_polymorphic_type]->get_vtable()) first_polymorphic_type++;
}

python_type_metadata *pymetadata_global::search_type_by_vtable(void **vtable)
{
    int ini=first_polymorphic_type, end=n_type;
    while(end>ini)
    {
	int mid=(ini+end)>>1;
	if(type[mid]->get_vtable()>=vtable) end=mid; else ini=mid+1;
    }
    if(ini<n_type && type[ini]->get_vtable()==vtable) return type[ini];
    return NULL;
}

bool pymetadata_global::check_type(python_type_metadata *type_)
{
    for(int i=0; i<n_type; i++) if(type[i]==type_) return true;
    return false;
}

int pymetadata_global::add_type(python_type_metadata *type_)
{
    int i=util::inc_vector(type, n_type, max_type, INC_TYPE);
    type[i]=type_;

    // special types
    if(!strcmp(type_->get_name(), "pyintromit.python_vector")) type_python_vector=type_;

    return i;
}

void pymetadata_global::dump_type_list()
{
    for(int i=0; i<n_type; i++)
	printf("%3d %p %s\n", i, type[i], type[i]->get_name());
}

int pymetadata_global::add_dl(char *elf_name, void *dl_)
{
    int i=util::inc_vector(dl, n_dl, max_dl, INC_DL);
    dl[i].elf_name=strdup(elf_name);
    dl[i].dl=dl_;
    return i;
}

void pymetadata_global::clear_error_string()
{
    n_error_string=0;
}

void pymetadata_global::add_error_string(const char *format, ...)
{
    int size=256;
    while(1)
    {
	if(n_error_string+size>max_error_string)
	{
	    max_error_string=n_error_string+size;
	    error_string=(char*)realloc(error_string, max_error_string+1);
	}

	va_list ap;
	va_start(ap, format);
	int real_size=vsnprintf(&error_string[n_error_string], size, format, ap);
	va_end(ap);
	if(real_size<0) return;
	else if(real_size>=size) size<<=1;
	else
	{
	    // ok, str added
	    n_error_string+=real_size;
	    return;
	}
    }
}

char *pymetadata_global::python_error_string(PyObject **exception)
{
    PyObject *type_err, *value_err, *traceback_err;
    PyErr_Fetch(&type_err, &value_err, &traceback_err);
    if(!value_err || !PyString_Check(value_err))
    {
	if(exception) *exception=PyExc_TypeError;
	return const_cast<char*>("[error string not set]");
    }
    if(exception) *exception=type_err;
    return PyString_AsString(value_err);
}

PyObject *pymetadata_global::c_to_python_float(void *type_pointer, int *data)
{
    return PyFloat_FromDouble(*(float*)type_pointer);
}

int pymetadata_global::python_to_c_float(PyObject *value, void *type_pointer, int *data)
{
    double d;
    if((d=PyFloat_AsDouble(value))==-1 && PyErr_Occurred()) return -1;
    *(float*)type_pointer=d;
    return 0;
}

int pymetadata_global::check_python_float(PyObject *value, int *data)
{
    return (check_python_ulong_long(value, data) || check_python_long_long(value, data) || PyFloat_Check(value));
}

PyObject *pymetadata_global::c_to_python_double(void *type_pointer, int *data)
{
    return PyFloat_FromDouble(*(double*)type_pointer);
}

int pymetadata_global::python_to_c_double(PyObject *value, void *type_pointer, int *data)
{
    double d;
    if((d=PyFloat_AsDouble(value))==-1 && PyErr_Occurred()) return -1;
    *(double*)type_pointer=d;
    return 0;
}

int pymetadata_global::check_python_double(PyObject *value, int *data)
{
    return (check_python_ulong_long(value, data) || PyFloat_Check(value));
}

PyObject *pymetadata_global::c_to_python_int(void *type_pointer, int *data)
{
    return PyInt_FromLong(*(int*)type_pointer);
}

int pymetadata_global::python_to_c_int(PyObject *value, void *type_pointer, int *data)
{
    int i;
    if((i=PyInt_AsLong(value))==-1 && PyErr_Occurred()) return -1;
    *(int*)type_pointer=i;
    return 0;
}

int pymetadata_global::check_python_int(PyObject *value, int *data)
{
    return PyInt_Check(value);
}

PyObject *pymetadata_global::c_to_python_unsigned_int(void *type_pointer, int *data)
{
    unsigned u=*(unsigned*)type_pointer;
    if(u>2147483647)
	return PyLong_FromUnsignedLong(u); // must return a python long
    else
	return PyInt_FromLong(u);
}

int pymetadata_global::python_to_c_unsigned_int(PyObject *value, void *type_pointer, int *data)
{
    unsigned u;
    u=PyLong_AsUnsignedLong(value);
    if(PyErr_Occurred()) return -1;
    *(unsigned*)type_pointer=u;
    return 0;
}

int pymetadata_global::check_python_unsigned_int(PyObject *value, int *data)
{
    if(!(PyInt_Check(value) || PyLong_Check(value))) return 0;
    unsigned u;
    u=PyLong_AsUnsignedLong(value);
    if(PyErr_Occurred()) {PyErr_Clear(); return 0;}
    return 1;
}

PyObject *pymetadata_global::c_to_python_short(void *type_pointer, int *data)
{
    return PyInt_FromLong(*(short*)type_pointer);
}

int pymetadata_global::python_to_c_short(PyObject *value, void *type_pointer, int *data)
{
    int i;
    if((i=PyInt_AsLong(value))==-1 && PyErr_Occurred()) return -1;
    if(i>32767 || i<-32768)
    {
	PyErr_SetString(PyExc_OverflowError, "value out of bounds for C/C++ short(16b) storage");
	return -1;
    }
    *(short*)type_pointer=i;
    return 0;
}

int pymetadata_global::check_python_short(PyObject *value, int *data)
{
    if(!PyInt_Check(value)) return 0;
    int i;
    if((i=PyInt_AsLong(value))==-1 && PyErr_Occurred()) return 0;
    if(i>32767 || i<-32768) return 0;
    return 1;
}

PyObject *pymetadata_global::c_to_python_unsigned_short(void *type_pointer, int *data)
{
    return PyInt_FromLong(*(unsigned short*)type_pointer);
}

int pymetadata_global::python_to_c_unsigned_short(PyObject *value, void *type_pointer, int *data)
{
    int i;
    if((i=PyInt_AsLong(value))==-1 && PyErr_Occurred()) return -1;
    if(i>65535 || i<0)
    {
	PyErr_SetString(PyExc_OverflowError, "value out of bounds for C/C++ ushort(16b) storage");
	return -1;
    }
    *(unsigned short*)type_pointer=i;
    return 0;
}

int pymetadata_global::check_python_unsigned_short(PyObject *value, int *data)
{
    if(!PyInt_Check(value)) return 0;
    int i;
    if((i=PyInt_AsLong(value))==-1 && PyErr_Occurred()) return 0;
    if(i>65535 || i<0) return 0;
    return 1;
}

PyObject *pymetadata_global::c_to_python_long_long(void *type_pointer, int *data)
{
    return PyLong_FromLongLong(*(long long*)type_pointer);
}

int pymetadata_global::python_to_c_long_long(PyObject *value, void *type_pointer, int *data)
{
    long long i;
    if((i=PyLong_AsLongLong(value))==-1 && PyErr_Occurred()) return -1;
    *(long long*)type_pointer=i;
    return 0;
}

int pymetadata_global::check_python_long_long(PyObject *value, int *data)
{
    if(!(PyLong_Check(value) || PyInt_Check(value))) return 0;
    long long i;
    if((i=PyLong_AsLongLong(value))==-1 && PyErr_Occurred()) {PyErr_Clear(); return 0;}
    return 1;
}

PyObject *pymetadata_global::c_to_python_ulong_long(void *type_pointer, int *data)
{
    return PyLong_FromUnsignedLongLong(*(unsigned long long*)type_pointer);
}

int pymetadata_global::python_to_c_ulong_long(PyObject *value, void *type_pointer, int *data)
{
    unsigned long long u;
    // this first step is to correct the following error of python when trying
    // to convert from PyLong to UnsignedLongLong:
    // SystemError: ../Objects/longobject.c:975: bad argument to internal function
    u=PyLong_AsUnsignedLong(value);
    if(PyErr_Occurred())
    {
	PyErr_Clear();
	u=PyLong_AsUnsignedLongLong(value);
	if(PyErr_Occurred()) return -1;
    }
    *(unsigned long long*)type_pointer=u;
    return 0;
}

int pymetadata_global::check_python_ulong_long(PyObject *value, int *data)
{
    if(!(PyLong_Check(value) || PyInt_Check(value))) return 0;
    unsigned long long u;
    u=PyLong_AsUnsignedLong(value);
    if(PyErr_Occurred())
    {
	PyErr_Clear();
	u=PyLong_AsUnsignedLongLong(value);
	if(PyErr_Occurred()) {PyErr_Clear(); return 0;}
    }
    return 1;
}

PyObject *pymetadata_global::c_to_python_boolean(void *type_pointer, int *data)
{
    return PyBool_FromLong(*(bool*)type_pointer);
}

int pymetadata_global::python_to_c_boolean(PyObject *value, void *type_pointer, int *data)
{
    if(!PyBool_Check(value))
    {
	PyErr_SetString(PyExc_TypeError, "not of boolean type");
	return -1;
    }

    if(PyInt_AsLong(value)) *(bool*)type_pointer=true; else *(bool*)type_pointer=false;
    return 0;
}

int pymetadata_global::check_python_boolean(PyObject *value, int *data)
{
    return PyBool_Check(value);
}

PyObject *pymetadata_global::c_to_python_char(void *type_pointer, int *data)
{
    return PyInt_FromLong((int)(*(char*)type_pointer));
}

int pymetadata_global::python_to_c_char(PyObject *value, void *type_pointer, int *data)
{
    int i;
    if((i=PyInt_AsLong(value))==-1 && PyErr_Occurred()) return -1;
    if(i>127 || i<-128)
    {
	PyErr_SetString(PyExc_OverflowError, "value out of bounds for C/C++ char(8b) storage");
	return -1;
    }
    *(char*)type_pointer=(char)i;
    return 0;
}

int pymetadata_global::check_python_char(PyObject *value, int *data)
{
    if(!PyInt_Check(value)) return 0;
    int i;
    if((i=PyInt_AsLong(value))==-1 && PyErr_Occurred()) return 0;
    if(i>127 || i<-128) return 0;
    return 1;
}

PyObject *pymetadata_global::c_to_python_unsigned_char(void *type_pointer, int *data)
{
    return PyInt_FromLong((int)(*(unsigned char*)type_pointer));
}

int pymetadata_global::python_to_c_unsigned_char(PyObject *value, void *type_pointer, int *data)
{
    int i;
    if((i=PyInt_AsLong(value))==-1 && PyErr_Occurred()) return -1;
    if(i>255 || i<0)
    {
	PyErr_SetString(PyExc_OverflowError, "value out of bounds for C/C++ uchar(8b) storage");
	return -1;
    }
    *(unsigned char*)type_pointer=(unsigned char)i;
    return 0;
}

int pymetadata_global::check_python_unsigned_char(PyObject *value, int *data)
{
    if(!PyInt_Check(value)) return 0;
    int i;
    if((i=PyInt_AsLong(value))==-1 && PyErr_Occurred()) return 0;
    if(i>255 || i<0) return 0;
    return 1;
}

int pymetadata_global::common_object_pointer(int *data, python_type_metadata **type, int **data_obj, int *has_reference)
{
    // this first condition is for special global variable case
    if(*data==GENDATA_CLASS) *data_obj=data;

    // point data to object metadata
    else if(!(*data_obj=gendata_resolve_pos_type(data)))
    {
	PyErr_SetString(PyExc_TypeError, "Internal: Pointer to object metadata w/o type");
	return 0;
    }

    // python type
    *type=(python_type_metadata *)gendata_get_data(*data_obj, GENDATA_GENDATA_RESERVE);
    if(!*type)
    {	// TODO: remove this check!
	PyErr_SetString(PyExc_TypeError, "Internal: No metadata type in metadata structure");
	return 0;
    }

    // check if C++ objects are reference counted
    if(has_reference)
    {
	if((*type)->refcnt_location!=GENDATA_INVALID_VALUE) *has_reference=1; else *has_reference=0;
    }
    return 1;
}

PyObject *pymetadata_global::c_to_python_object_pointer(void *type_pointer, int *data)
{
    // C++ Object
    void *obj=*(void**)type_pointer;
    if(!obj) Py_RETURN_NONE;

    // get object metadata and type
    int *data_obj;
    python_type_metadata *type;
    int has_reference;
    if(!common_object_pointer(data, &type, &data_obj, &has_reference)) return NULL;

    // when has reference count, make a new reference
    if(has_reference)
    {
	// create new (or new reference of) P1 type python_metadata object
	python_metadata *python_obj;
	if(!(python_obj=python_metadata::new_python_metadata(obj, 0/*indirection*/, NULL/*obj_container*/, type, NULL/*ctor_args*/)))
	    PyErr_SetString(PyExc_TypeError, "Internal: cannot create new python metadata");
	return (PyObject*)python_obj;
    }
    else
    {
	// search obj_container
	python_metadata *python_obj;
	python_metadata *python_obj_container;
	if(!(python_obj_container=pymdg.search_object_container(type_pointer, NULL/*i_destination*/)))
	{
	    // the pointer (type_pointer) is not part of a registered pyobject
	    // the pointer may reside in stack and the C object is:
	    //   1. an already registered pyobject -> return a new reference of P1, P2 or P3
	    //   2. a C object contained in other object -> return a P3
	    //   3. may be a newly allocated object returned by a function or a member of an unknown pyobject ->
	    //       -> create a new P1 or P2 (warning, when container registered, convert this to P3 or P4)
	    if((python_obj=pymdg.search_object(obj, type)))
	    {
		// case 1
		python_obj->python_incref();
		return (PyObject*)python_obj;
	    }
	    if((python_obj_container=pymdg.search_object_container(obj, NULL/*i_destination*/)))
	    {
		// case 2: create or rereferenciate a P3
		if(!(python_obj=python_metadata::new_python_metadata(obj, 0/*indirection*/, python_obj_container, type, NULL/*ctor_args*/)))
		    PyErr_SetString(PyExc_TypeError, "Internal: cannot create new python metadata");
		return (PyObject*)python_obj;
	    }
	    // TODO: case 3, need to think about it

	    // DEBUG
	    ERROR("cannot get container of %p (& %p) in:", type_pointer, obj);
	    pymdg.dump_object_list(NULL);

	    PyErr_SetString(PyExc_TypeError, "Internal: Cannot get C++ container object");
	    return NULL;
	}

	// create new (or new reference of) P4 type python_metadata object
	if(!(python_obj=python_metadata::new_python_metadata(type_pointer, 1/*indirection*/, python_obj_container, type, NULL/*ctor_args*/)))
	    PyErr_SetString(PyExc_TypeError, "Internal: cannot create new python metadata");
	return (PyObject*)python_obj;
    }
}

int pymetadata_global::python_to_c_object_pointer(PyObject *value, void *type_pointer, int *data)
{
    // C++ Object
    void *obj=*(void**)type_pointer;

    // get object metadata and type
    int *data_obj;
    python_type_metadata *type;
    int has_reference;
    if(!common_object_pointer(data, &type, &data_obj, &has_reference)) return -1;

    // check python object type
    python_metadata *pyvalue;
    if(value && value!=Py_None) pyvalue=(python_metadata*)value; else pyvalue=NULL;
    if(pyvalue && !((python_type_metadata*)value->ob_type)->check_type_inheritance(type))
    {
	PyErr_SetString(PyExc_TypeError, "Object of different type");
	return -1;
    }

    // try to find the C++ object where the pointer resides and its python wrapper/container
    python_metadata *pyobj=pymdg.search_object_container(type_pointer);
    if(!pyobj)
    {
	// this is a parameter and going to put pointer_to_obj in the stack
	// reset obj
	obj=NULL;
    }

    // reference count?
    if(has_reference)
    {
	// increase C++ object refcount
	if(pyvalue)
	{
	    if(pyvalue->get_obj_container())
	    {
		// pyvalue is of P3 type
		// cannot assign pointers to a contained object, so throw exception
		PyErr_SetString(PyExc_TypeError, "procedural: sorry, cannot assign object pointer to a embeded object");
		return -1;
	    }
	    pyvalue->c_incref();
	}

	// free old object
	if(obj && !free_c_object_pointer(type_pointer, data)) return -1;

	// set new one
	if(pyvalue) *(void**)type_pointer=pyvalue->get_obj();
	return 0;
    }
    else
    {
#if 0
	// DEBUG2
	ERROR("TODO1 ---");
	pymdg.dump_object_list(pyvalue);
	pymdg.dump_object_list(pyobj);
#endif

	// 0, check that value is P2 or None
	if(pyvalue && pyvalue->get_obj_container())
	{
	    PyErr_SetString(PyExc_TypeError, "procedural: sorry, cannot assign a non P2 object to this one");
	    return -1;
	}

	// 1, check if there is already a P4 python object
	python_metadata *pyold;
	int must_decrease_python_count=0, i_pyold;
	if((pyold=pymdg.search_object(type_pointer, type, &i_pyold)))
	{
	    // there is already a P4 for this C++ object
	    // check that it's P4 actually
	    if(!pyold->get_obj_container() || !pyold->get_indirection())
	    {
		PyErr_SetString(PyExc_TypeError, "internal: old python_metadata should be a P4");
		return -1;
	    }
	    if(!obj)
	    {
		PyErr_SetString(PyExc_TypeError, "internal: NULL obj when converting to P2");
		return -1;
	    }

	    // must convert this P4 to a P2

	    // 1.1a, unregister object
	    if(!pymdg.remove_object(i_pyold)) ERROR("internal: pyold not registered when trying to remove it");

	    // 1.2, convert P4 to P2
	    pyold->transmute_object_type(obj);
	    must_decrease_python_count++;

	    // 1.5, register obj
	    if(pymdg.add_object(pyold)<0) ERROR("internal: adding pyold");
	}
	else
	    if(obj) type->cdealloc(obj);

	// 2, assign addr of pyvalue C++ object
	*(void**)type_pointer=pyvalue?pyvalue->get_obj():NULL;

	if(pyvalue && pyobj/*if function param, do not convert to P4*/)
	{
	    // 3, unregister obj
	    if(!pymdg.remove_object(pyvalue)) ERROR("internal: pyvalue not registered when trying to remove it");

	    // 4, convert P2 into P4
	    pyvalue->transmute_object_type(1/*indirection*/, (int)type_pointer-(int)pyobj->get_obj(), pyobj);
	    pyobj->python_incref();

	    // 5, register new object
	    if(pymdg.add_object(pyvalue)<0) ERROR("internal: adding pyvalue");
	}

	// 6, decrease count if there was a P3 for contained obj
	if(must_decrease_python_count) pyobj->python_decref();

#if 0
	// DEBUG2
	ERROR("TODO2 ---");
	pymdg.dump_object_list(pyvalue);
	pymdg.dump_object_list(pyobj);
#endif

	return 0;
    }
}

int pymetadata_global::check_python_object_pointer(PyObject *value, int *data)
{
    // get object metadata and type
    int *data_obj;
    python_type_metadata *type;
    if(!common_object_pointer(data, &type, &data_obj)) return 0;

    // check python object type: TODO inheritance
    if(value && (python_type_metadata*)value->ob_type!=type) return 0; else return 1;
}

int pymetadata_global::free_c_object_pointer(void *type_pointer, int *data)
{
    // C++ Object
    void *obj=*(void**)type_pointer;
    if(!obj) return 1;

    // get object metadata and type
    int *data_obj;
    python_type_metadata *type;
    int has_reference;
    if(!common_object_pointer(data, &type, &data_obj, &has_reference)) return 0;

    // reference count?
    if(has_reference)
    {
	python_metadata::c_decref(obj, type);
	*(void**)type_pointer=NULL;
	return 1;
    }
    else
    {
	// TODO: dont know what to do here!
	return 1;
    }
}

PyObject *pymetadata_global::c_to_python_class(void *type_pointer, int *data)
{
    // python type
    python_type_metadata *type=(python_type_metadata *)gendata_get_data(data, GENDATA_GENDATA_RESERVE);
    if(!type)
    {	// TODO: remove this check!
	PyErr_SetString(PyExc_TypeError, "Internal: No metadata type in metadata structure");
	return NULL;
    }

    python_metadata *python_obj_container, *python_obj;
    if((python_obj_container=pymdg.search_object_container(type_pointer, NULL/*i_destination*/)))
    {
	// create or rereferenciate a P3
	if(!(python_obj=python_metadata::new_python_metadata(type_pointer, 0/*indirection*/, python_obj_container, type, NULL/*ctor_args*/)))
	    PyErr_SetString(PyExc_TypeError, "Internal: cannot create new python metadata");
	return (PyObject*)python_obj;
    }

    PyErr_SetString(PyExc_TypeError, "Internal: TODO c_to_python_class");
    return NULL;    
}

int pymetadata_global::python_to_c_class(PyObject *value, void *type_pointer, int *data)
{
    // pyobj is self in python_type_metadata::pyset_member_object()
    python_metadata *pyobj;
    if(!(pyobj=pymdg.search_object_container(type_pointer, NULL/*i_destination*/)))
    {
	PyErr_SetString(PyExc_TypeError, "cannot obtain object container for type_pointer");
	return -1;
    }

    return python_type_metadata::pyset_member_object((PyObject *)pyobj, value, type_pointer, data);
}

int pymetadata_global::check_python_class(PyObject *value, int *data)
{
    return 0;
}

int pymetadata_global::common_reference(int *data, int **data_obj)
{
    // point data to object metadata
    if(!(*data_obj=gendata_resolve_pos_type(data)))
    {
	PyErr_SetString(PyExc_TypeError, "Internal: reference metadata w/o type");
	return 0;
    }
    return 1;
}

PyObject *pymetadata_global::c_to_python_reference(void *type_pointer, int *data)
{
    // get referenced type metadata and type
    int *data_obj;
    if(!common_reference(data, &data_obj)) return NULL;

    // get c_to_python function for type
    md_c_to_python_func ctp;
    if(!(ctp=pymdg.get_func_c_to_python(*data_obj)))
    {
	pymdg.clear_error_string();
	pymdg.add_error_string("Internal: TODO: c_to_python function for type %s", gendata_type_strings[*data_obj]);
	PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
	return NULL;
    }

    // call c_to_python for subtype
    return ctp(*(void**)type_pointer, data_obj);
}

int pymetadata_global::python_to_c_reference(PyObject *value, void *type_pointer, int *data)
{
    // get referenced type metadata and type
    int *data_obj;
    if(!common_reference(data, &data_obj)) return -1;

    // get python_to_c function for type
    md_python_to_c_func ptc;
    if(!(ptc=pymdg.get_func_python_to_c(*data_obj)))
    {
	pymdg.clear_error_string();
	pymdg.add_error_string("Internal: TODO: python_to_c function for type %s", gendata_type_strings[*data_obj]);
	PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
	return -1;
    }

    // get size of the type
    int size;
    if((size=gendata_get_data(data_obj, GENDATA_SIZE))<=0)
    {
	pymdg.clear_error_string();
	pymdg.add_error_string("Internal: no size for type %s", gendata_type_strings[*data_obj]);
	PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
	return -1;
    }

    // must get space for the type (and store pointer)
    void *alloc=malloc(size); // freed in free function
    *(void **)type_pointer=alloc;
    return ptc(value, alloc, data_obj);
}

int pymetadata_global::check_python_reference(PyObject *value, int *data)
{
    // get referenced type metadata and type
    int *data_obj;
    if(!common_reference(data, &data_obj)) return 0;

    // get check function for type
    md_check_python_func check;
    if(!(check=pymdg.get_func_check_python(*data_obj)))
    {
	pymdg.clear_error_string();
	pymdg.add_error_string("Internal: TODO: check function for type %s", gendata_type_strings[*data_obj]);
	PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
	return 0;
    }

    // check referenced type
    return check(value, data_obj);
}

int pymetadata_global::free_c_reference(void *type_pointer, int *data)
{
    // get referenced type metadata and type
    int dev=1;
    int *data_obj;
    if(!common_reference(data, &data_obj)) return 0;

    // get free_c function for type
    md_free_c_func free_func;
    void *alloc;
    if((alloc=*(void**)type_pointer) && (free_func=pymdg.get_func_free_c(*data_obj)) && !free_func(alloc, data_obj))
    {
	fprintf(stderr, "internal: freeing type %s\n", gendata_type_strings[*data_obj]);
	dev=0;
    }

    // free allocated memory
    if(alloc)
    {
	free(alloc);
	*(void**)type_pointer=NULL;
    }
    return dev;
}

PyObject *pymetadata_global::c_to_python_charp(void *type_pointer, int *data)
{
    const char *str;
    if(!(str=*(const char**)type_pointer)) Py_RETURN_NONE;
    return PyString_FromString(str);
}

int pymetadata_global::python_to_c_charp(PyObject *value, void *type_pointer, int *data)
{
    if(!free_c_charp(type_pointer, data)) return -1;
    if(value==Py_None) *(char **)type_pointer=NULL;
    else if(PyString_Check(value)) *(char **)type_pointer=strdup(PyString_AsString(value));
    else return -1;
    return 0;
}

int pymetadata_global::check_python_charp(PyObject *value, int *data)
{
    // None or String are allowed
    if(value!=Py_None && !PyString_Check(value)) return 0; else return 1;
}

int pymetadata_global::free_c_charp(void *type_pointer, int *data)
{
    char *str;
    if((str=*(char**)type_pointer))
    {
	free(str);
	*(char**)type_pointer=NULL;
    }
    return 1;
}

PyObject *pymetadata_global::c_to_python_const_charp(void *type_pointer, int *data)
{
    const char *str;
    if(!(str=*(const char**)type_pointer)) Py_RETURN_NONE;
    return PyString_FromString(str);
}

int pymetadata_global::python_to_c_const_charp(PyObject *value, void *type_pointer, int *data)
{
    if(value==Py_None) *(char **)type_pointer=NULL;
    else if(PyString_Check(value))
    {
	// TODO: the string will persist as long as value persist, so take a create a ref by now
	// need to destroy such pyobjects
	Py_INCREF(value);
	*(char **)type_pointer=PyString_AsString(value);
    }
    else return -1;
    return 0;
}

int pymetadata_global::check_python_const_charp(PyObject *value, int *data)
{
    // None or String are allowed
    if(value!=Py_None && !PyString_Check(value)) return 0; else return 1;
}

int pymetadata_global::common_array_type(int *data)
{
    int *child=data+gendata_get_position(data, GENDATA_GENDATA_VAR)+1;
    if(*child!=GENDATA_SUBRANGE) return -1;
    return gendata_get_data(child, GENDATA_UPPER_BOUND);
}

PyObject *pymetadata_global::c_to_python_array_type(void *type_pointer, int *data)
{
    // get item type metadata
    int *data_obj;
    if(!common_reference(data, &data_obj)) return NULL;

    // char [] special case
    if(*data_obj==GENDATA_CHAR) return PyString_FromString((char*)type_pointer);

    // get c_to_python function for type
    md_c_to_python_func ctp;
    if(!(ctp=pymdg.get_func_c_to_python(*data_obj)))
    {
	pymdg.clear_error_string();
	pymdg.add_error_string("Internal: TODO: c_to_python function for type %s", gendata_type_strings[*data_obj]);
	PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
	return NULL;
    }

    // get size of array and element size
    int n, size;
    if((n=common_array_type(data))<0 || (size=gendata_get_data(data_obj, GENDATA_SIZE))<0)
    {
	PyErr_SetString(PyExc_TypeError, "Internal: array_type w/o or element size <0 subrange");
	return NULL;
    }
    n++;

    // generate list
    PyObject *list=PyList_New(n);
    for(int i=0; i<n; i++)
    {
	PyObject *obj=ctp(type_pointer, data_obj);
	if(!obj)
	{
	    // delete list
	    Py_DECREF(list);

	    // make an elaborated error string
	    PyObject *exception;
	    pymdg.clear_error_string();
	    pymdg.add_error_string("element at position %d: %s", i, pymdg.python_error_string(&exception));
	    PyErr_SetString(exception, pymdg.get_error_string());
	    return NULL;
	}
	PyList_SetItem(list, i, obj);
	type_pointer=(char*)type_pointer+size;
    }
    return list;
}

int pymetadata_global::python_to_c_array_type(PyObject *value, void *type_pointer, int *data)
{
    // check value
    if(!check_python_array_type(value, data))
    {
	PyErr_SetString(PyExc_TypeError, "a list is required (or string)");
	return -1;
    }

    // get item type metadata
    int *data_obj;
    if(!common_reference(data, &data_obj)) return -1;

    // get size of array and element size
    int n, size;
    if((n=common_array_type(data))<0 || (size=gendata_get_data(data_obj, GENDATA_SIZE))<0)
    {
	PyErr_SetString(PyExc_TypeError, "Internal: array_type w/o or element size <0 subrange");
	return -1;
    }
    n++;

    // char [] special case
    if(*data_obj==GENDATA_CHAR)
    {
	char *str=const_cast<char*>("");
	if(value!=Py_None) str=PyString_AsString(value);
	strncpy((char*)type_pointer, str, n-1);
	if(strlen(str)>=(unsigned)n) ((char*)type_pointer)[n-1]=0;
	return 0;
    }

    // get python_to_c function for type
    md_python_to_c_func ptc;
    md_free_c_func fc;
    if(!(ptc=pymdg.get_func_python_to_c(*data_obj)))
    {
	pymdg.clear_error_string();
	pymdg.add_error_string("Internal: TODO: python_to_c function for type %s", gendata_type_strings[*data_obj]);
	PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
	return -1;
    }
    fc=pymdg.get_func_free_c(*data_obj);

    // sizes must be the same
    if(PyList_Size(value)!=n)
    {
	pymdg.clear_error_string();
	pymdg.add_error_string("list size must be %d (%d suplied)", n, PyList_Size(value));
	PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
	return -1;
    }

    // set values
    for(int i=0; i<n; i++)
    {
	void *pos_vector=(char*)type_pointer+i*size;

	// free old value
	if(fc) fc(pos_vector, data_obj);

	// set new one
	PyObject *obj=PyList_GetItem(value, i);
	if(!obj || ptc(obj, pos_vector, data_obj)<0)
	{
	    // make an elaborated error string
	    PyObject *exception;
	    pymdg.clear_error_string();
	    pymdg.add_error_string("element at position %d: %s", i, pymdg.python_error_string(&exception));
	    PyErr_SetString(exception, pymdg.get_error_string());
	    return -1;
	}
    }
    return 0;
}

int pymetadata_global::check_python_array_type(PyObject *value, int *data)
{
    // NULL value
    if(!value) return 0;

    // get item type metadata
    int *data_obj;
    if(!common_reference(data, &data_obj)) return 0;

    // char [] special case
    if(*data_obj==GENDATA_CHAR)
	return value==Py_None || PyString_Check(value);
    else
	return PyList_Check(value);
}

int pymetadata_global::free_c_array_type(void *type_pointer, int *data)
{
    // get item type metadata
    int *data_obj;
    if(!common_reference(data, &data_obj)) return 0;

    // get free function
    md_free_c_func fc;
    if((fc=pymdg.get_func_free_c(*data_obj)))
    {
	// get size of array and element size
	int n, size;
	if((n=common_array_type(data))<0 || (size=gendata_get_data(data_obj, GENDATA_SIZE))<0)
	{
	    PyErr_SetString(PyExc_TypeError, "Internal: array_type w/o or element size <0 subrange");
	    return 0;
	}
	n++;

	for(int i=0; i<n; i++)
	{
	    void *pos_vector=(char*)type_pointer+i*size;
	    fc(pos_vector, data_obj);
	}
    }
    return 1;
}

int pymetadata_global::add_object(python_metadata *obj, int i_destination)
{
    int i;
    if(i_destination>=0) i=i_destination; else i=search_object_index(obj->get_obj_woind());

    // if there are multiple pyobjects with the same woind(), go to the last one
    void *obj_woind=obj->get_obj_woind();
    while(i<n_obj_list && obj_list[i]->get_obj_woind()==obj_woind) i++;

    // add it
    util::inc_vector(obj_list, n_obj_list, max_obj_list, INC_OBJ_LIST);
    if(n_obj_list-i-1) memmove(&obj_list[i+1], &obj_list[i], (n_obj_list-i-1)*sizeof(python_metadata *));
    obj_list[i]=obj;

    return i;
}

int pymetadata_global::add_objects(python_metadata **obj, int n_obj)
{
    int i=search_object_index(obj[0]->get_obj_woind());

    // if there are multiple pyobjects with the same woind(), go to the last one
    void *obj_woind=obj[0]->get_obj_woind();
    while(i<n_obj_list && obj_list[i]->get_obj_woind()==obj_woind) i++;

    // add it
    for(int j=0; j<n_obj; j++)
	util::inc_vector(obj_list, n_obj_list, max_obj_list, INC_OBJ_LIST);
    if(n_obj_list-i-n_obj)
	memmove(&obj_list[i+n_obj], &obj_list[i], (n_obj_list-i-n_obj)*sizeof(python_metadata *));
    memcpy(&obj_list[i], obj, n_obj*sizeof(python_metadata *));

    return i;
}

python_metadata *pymetadata_global::search_object(void *obj, int *i_destination_if_added, int *n_at_addr)
{
    int i=search_object_index(obj);
    if(i_destination_if_added) *i_destination_if_added=i;
    if(n_at_addr)
    {
	*n_at_addr=0;
	int j=i;
	while(j<n_obj_list && obj_list[j]->get_obj_woind()==obj)
	{
	    (*n_at_addr)++;
	    j++;
	}
    }

    if(i<n_obj_list && obj_list[i]->get_obj_woind()==obj) return obj_list[i]; else return NULL;
}

python_metadata *pymetadata_global::search_object(void *obj, python_type_metadata *type, int *i_destination, int *n_at_addr)
{
    int n, i;
    python_metadata *pyobj=search_object(obj, &i, &n);
    if(i_destination) *i_destination=i;
    if(n_at_addr) *n_at_addr=n;
    if(!pyobj) return NULL;
    while(n)
    {
	if(obj_list[i]->get_python_type()==type)
	{
	    if(i_destination) *i_destination=i;
	    if(n_at_addr) *n_at_addr=n;
	    return obj_list[i];
	}
	n--; i++;
    }
    return NULL;
}

int pymetadata_global::search_object(python_metadata *pyobj)
{
    int i;
    if(!search_object(pyobj->get_obj_woind(), pyobj->get_python_type(), &i)) return -1;
    return i;
}

python_metadata *pymetadata_global::search_object_container(void *obj, int *i_destination_if_added)
{
    int i=search_object_index(obj);
    if(i_destination_if_added) *i_destination_if_added=i;

    if(i==n_obj_list || obj_list[i]->get_obj_woind()!=obj) i--;
    else
    {
	// obj_list[i]->woind == obj -> go to last with same address
	while(++i<n_obj_list && obj_list[i]->get_obj_woind()==obj);
	i--;
    }
    while(i>=0)
    {
	if(!obj_list[i]->get_obj_container() || !obj_list[i]->get_indirection())
	{
	    if((char*)obj_list[i]->get_obj_woind() + obj_list[i]->get_c_size() > obj) return obj_list[i];
	    if(!obj_list[i]->get_obj_container()) break; // do not continue after a P1 or P2
	}
	// else is a P4
	i--;
    }

    // exhaustive search in C++
    for(int j=0; j<n_obj_list; j++)
    {
	if(obj_list[j]->get_obj_container() && obj_list[j]->get_indirection() &&
	   obj_list[j]->get_obj() <= obj && (char*)obj_list[j]->get_obj() + obj_list[j]->get_c_size() > obj)
	    return obj_list[j];
    }

    return NULL;
}

void pymetadata_global::move_objects(int i_des, int i_ori, int n)
{
    if(n>MAX_STACK) ERROR_R("internal: maximum space for embedding object reached, increase MAX_STACK");
    if(i_ori==i_des) return;
    if(i_des>i_ori && i_des<=i_ori+n) ERROR_R("internal: overlapping zone");

    python_metadata *obj_list_stack[MAX_STACK];
    memcpy(obj_list_stack, &obj_list[i_ori], n*sizeof(python_metadata*));
    if(i_ori<i_des)
    {
	i_des-=n;
	memmove(&obj_list[i_ori], &obj_list[i_ori+n], (i_des-i_ori)*sizeof(python_metadata*));
    }
    else
	memmove(&obj_list[i_des+n], &obj_list[i_des], (i_ori-i_des)*sizeof(python_metadata*));
    memcpy(&obj_list[i_des], obj_list_stack, n*sizeof(python_metadata*));
}

int pymetadata_global::search_object_index(void *obj)
{
    // check vanished
    if(n_obj_list_vanished>n_obj_list) n_obj_list_vanished=n_obj_list;
    for(int i=0; i<n_obj_list_vanished; i++)
	if(obj_list[i]->get_obj_woind())
	{
	    // object recovered
	    sort_object_list();
	    break;
	}

    // search between not vanished
    int ini=n_obj_list_vanished, end=n_obj_list;
    while(end>ini)
    {
	int mid=(ini+end)>>1;
	void *woind=obj_list[mid]->get_obj_woind();
	if(!woind)
	{
	    // at least one object vanished
	    sort_object_list();
	    return search_object_index(obj);
	}
	if(woind>=obj) end=mid; else ini=mid+1;
    }
    return ini;
}

int pymetadata_global::remove_object(python_metadata *obj)
{
    // ERROR("DELETED: %p %p", obj, obj->get_obj_woind());

    int i, n;
    if(!search_object(obj->get_obj_woind(), &i, &n))
    {
	if(obj->get_obj_container() && obj->get_indirection())
	{
	    // this is a P4 whose references have vanished, delete by obj
	    for(i=0; i<n_obj_list; i++)
		if(obj_list[i]==obj)
		    return remove_object(i);
	    ERROR_R0("P4 object not found");
	}

	// very possible woind unsort case
	if(!check_object_list())
	{
	    sort_object_list();
	    return remove_object(obj);
	}

	ERROR_R0("obj not found when trying to remove it");
    }

    // test all posibilities
    while(n) {if(obj_list[i]==obj) break; n--; i++;}
    if(!n)
	ERROR_R0("several posibilities at object removal, but none matched: Py %p   C++ %p",
		 obj, obj->get_obj_woind());

    // delete object at position i
    return remove_object(i);
}

int pymetadata_global::remove_object(int i, int n, python_metadata **copy)
{
    // copy removed objects if requested
    if(copy) memcpy(copy, &obj_list[i], n*sizeof(python_metadata *));

    if(n_obj_list-i-n) memmove(&obj_list[i], &obj_list[i+n], (n_obj_list-i-n)*sizeof(python_metadata *));
    n_obj_list-=n;
    return 1;
}

int pymetadata_global::n_contained_objects(int i_obj)
{
    int n=0;
    void *last_addr=(char*)obj_list[i_obj]->get_obj_woind()+obj_list[i_obj]->get_c_size();
    while(++i_obj<n_obj_list && obj_list[i_obj]->get_obj_woind()<last_addr) n++;
    return n;
}

void pymetadata_global::dump_object_list(python_metadata *obj)
{
    int stack[MAX_STACK], i_stack=0;
    printf("%6d  "WHITE"  Python"MAGENTA"       C++ ""WoInd C++ "RED"container"GREEN" C++ refcnt"WHITE" C++ size"
	   MAGENTA" PyT"RED" Type"RESET"\n", n_obj_list);
    for(int i=0; i<80; i++) printf("-"); printf("\n");
    for(int i=0; i<n_obj_list; i++)
    {
	printf("%6d  "WHITE"%08x  "MAGENTA"%08x  ""%08x  "RED"%08x   "GREEN"%8d "WHITE"%8d"MAGENTA"  P%d"RED" ", i,
	       (unsigned int)obj_list[i], (unsigned int)obj_list[i]->get_obj(), (unsigned int)obj_list[i]->get_obj_woind(),
	       (unsigned int)obj_list[i]->get_obj_container(), obj_list[i]->has_refcnt()?obj_list[i]->get_refcnt():0,
	       obj_list[i]->get_c_size(), 1+(int)obj_list[i]->get_obj_type());

	// do indentation process
	while(i_stack)
	{
	    void *addr=(char*)obj_list[stack[i_stack-1]]->get_obj_woind()+obj_list[stack[i_stack-1]]->get_c_size();
	    if(obj_list[i]->get_obj_woind() < addr) break;
	    i_stack--;
	}
	stack[i_stack++]=i;

#if 0
	if(!i_stack) stack[i_stack++]=i;
	else
	{
	    for(int j=i_stack-1; j>=0; j--)
	    {
		void *addr=(char*)obj_list[stack[j]]->get_obj_woind()+obj_list[stack[j]]->get_c_size();
		if(obj_list[i]->get_obj_woind() < addr)
		{
		    i_stack=j+1;
		    stack[i_stack++]=i;
		    break;
		}
	    }
	}
#endif

	for(int j=1; j<i_stack; j++) printf("  ");

	printf("%s", obj_list[i]->get_type_string());
	if(obj_list[i]==obj) printf(WHITE" <--"RESET"\n"); else printf(RESET"\n");
    }
    printf("\n");
    if(!check_object_list())
    {
	printf(WHITE"ERROR, woind not sorted!"RESET"\n\n");
	sort_object_list();
	if(!check_object_list()) printf(WHITE"ERROR, internal check or sort error"RESET"\n\n");
    }
}

bool pymetadata_global::check_object_list()
{
    for(int i=1; i<n_obj_list; i++)
	if(obj_list[i-1]->get_obj_woind()>obj_list[i]->get_obj_woind()) return false;
    return true;
}

int pymetadata_global::sort_object_list_by_woind(const void *a, const void *b)
{
    return ((unsigned)(*(python_metadata**)b)->get_obj_woind() < (unsigned)(*(python_metadata**)a)->get_obj_woind());
}

void pymetadata_global::sort_object_list()
{
    qsort(obj_list, n_obj_list, sizeof(python_metadata *), sort_object_list_by_woind);

    // count vanished
    for(int i=0; i<n_obj_list; i++)
	if(obj_list[i]->get_obj_woind())
	{
	    n_obj_list_vanished=i;
	    return;
	}
    n_obj_list_vanished=n_obj_list;
}

bool pymetadata_global::event_queue_add(python_metadata *obj, const char *method_name)
{
    // check
    if(!obj || !method_name) return false;

    // search function entry point
    python_type_metadata *type=obj->get_python_type();
    void *func;
    if(!(func=type->search_method_entry_point(method_name))) return false; // not found

    // test if object and function already added
    for(int i=0; i<n_eq_obj; i++)
	if(eq_obj[i].obj==obj && !strcmp(method_name, eq_obj[i].func_name)) return false; // aready added

    // check structure holes
    int i;
    for(i=0; i<n_eq_obj; i++) if(!eq_obj[i].obj) break;
    if(i==n_eq_obj) i=util::inc_vector(eq_obj, n_eq_obj, max_eq_obj, INC_EQ_OBJ);
    eq_obj[i].obj=obj;
    eq_obj[i].func_name=strdup(method_name);
    eq_obj[i].func=(int(*)(void*))func;

    // take a reference of the object
    obj->python_incref();

    // add obj to event queue for the first time
    eq_add(i, eq_time_now());

    return true;
}

bool pymetadata_global::event_queue_remove(python_metadata *obj, const char *method_name)
{
    bool dev=false;
    for(int i=0; i<n_eq_obj; i++)
    {
	if(!eq_obj[i].obj) continue; // hole
	if(!obj || (eq_obj[i].obj==obj && (!method_name || !strcmp(method_name, eq_obj[i].func_name))))
	{
	    // remove this object
	    eq_obj[i].obj->python_decref();
	    eq_obj[i].obj=NULL;
	    eq_obj[i].func_name=(char*)realloc(eq_obj[i].func_name, 0);

	    // remove events of i
	    if(eq_ini!=eq_end)
	    {
		int j=eq_ini;
		do
		{
		    j=(j-1)&EVENT_QUEUE_MASK;
		    if(eq[j].i_queue_obj==i) eq_del(j);
		}
		while(j!=eq_end);
	    }

	    dev=true;
	}
    }
    return dev;
}

bool pymetadata_global::event_queue_refresh(bool control_idle_time)
{
    if(eq_ini==eq_end)
    {
	// empty queue
	if(control_idle_time) usleep(USEC_INTERVAL);
    }
    else
    {
	// events in queue
	unsigned time_now=eq_time_now(), time_next_event=eq[eq_end].time;

	if(eq_time_compare(time_now, time_next_event))
	{
	    // time reached for this event, execute
	    int i_queue_obj=eq[eq_end].i_queue_obj;
	    event_queue_object &eqo=eq_obj[i_queue_obj];
	    int usec_refresh=eqo.func(eqo.obj->get_obj());
	    eq_extract_first();
	    if(usec_refresh>=0) eq_add(i_queue_obj, time_now+usec_refresh);
	    else event_queue_remove(eqo.obj, eqo.func_name);
	}
	else
	{
	    // have time until next event
	    if(control_idle_time)
	    {
		int diff=time_next_event-time_now;
		if(diff>USEC_INTERVAL)
		    usleep(USEC_INTERVAL);
		else
		    usleep(diff);
	    }
	}
    }
    return true;
}

void pymetadata_global::eq_extract_first()
{
    if(eq_ini!=eq_end) eq_end=(eq_end+1)&EVENT_QUEUE_MASK;
}

void pymetadata_global::eq_add(int i_queue_obj, unsigned time)
{
    if(!((eq_ini+1-eq_end) & EVENT_QUEUE_MASK)) ERROR_R("event queue full!");

    int i=eq_end;
    while(i!=eq_ini)
    {
	if(eq_time_compare(eq[i].time, time)) break;
	i=(i+1)&EVENT_QUEUE_MASK;
    }

    int j=eq_ini;
    int jr=(eq_ini-1)&EVENT_QUEUE_MASK;
    eq_ini=(eq_ini+1)&EVENT_QUEUE_MASK;
    while(i!=j)
    {
	memcpy(&eq[j], &eq[jr], sizeof(event_queue));
	j=jr;
	jr=(jr-1)&EVENT_QUEUE_MASK;
    }

    eq[i].i_queue_obj=i_queue_obj;
    eq[i].time=time;
}

void pymetadata_global::eq_del(int i)
{
    while((i+1-eq_ini)&EVENT_QUEUE_MASK)
    {
	memcpy(&eq[i], &eq[(i+1)&EVENT_QUEUE_MASK], sizeof(event_queue));
	i=(i+1)&EVENT_QUEUE_MASK;
    }
    eq_ini=(eq_ini-1)&EVENT_QUEUE_MASK;
}

unsigned pymetadata_global::eq_time_now()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec*1000000+tv.tv_usec;
}

bool pymetadata_global::eq_time_compare(unsigned time1, unsigned time2)
{
    return ((int)(time1-time2)>0);
}
